﻿using System;
using System.Linq;
using EPiServer.Core;
using EPiServer.Data;
using EPiServer.Data.Dynamic;
using EPiServer.DataAbstraction;
using EPiServer.DataAccess;
using System.Web;
using System.Collections.Generic;

namespace EPiServer.DdsPageProvider
{
	/// <summary>
	/// This page provider doesn't handle a lot of thing but it does save pages in the dynamic data store.
	/// </summary>
    public class DdsPageProvider : PageProviderBase
    {
        DynamicDataStoreFactory storeFactory;
        PropertyMapper propertyMapper;
        Func<PageTypeCollection> pageTypeSource;

        #region PageProviderBase Implementation

        public override PageProviderCapabilities Capabilities
        {
            get { return base.Capabilities | PageProviderCapabilities.Create | PageProviderCapabilities.Edit | PageProviderCapabilities.Delete | PageProviderCapabilities.Move; }
        }

        public override PageReference Save(PageData page, SaveAction action)
        {
			NodeRepository nodeRepository = NodeRepository();

			Node node = nodeRepository.GetOrCreateNode(page.PageGuid);
            node.UpdateWith(page);
			Identity nodeId = nodeRepository.Save(node);

			PropertyRepository propertyRepository = PropertyRepository(page.PageTypeID);
			PropertyBag bag = propertyRepository.GetOrCreatePropertyBag(nodeId);
            bag.UpdateWith(page);
			propertyRepository.Save(bag);

            return ToPageReference(node.Id);
        }

		private void SetPageStatus(PageData page, SaveAction action)
		{
			if ((action & SaveAction.Publish) == SaveAction.Publish)
			{
				base.SetPageStatus(page, VersionStatus.Published);
			}
			else if ((action & SaveAction.CheckIn) == SaveAction.CheckIn)
			{
				base.SetPageStatus(page, VersionStatus.CheckedIn);
			}
			else
			{
				base.SetPageStatus(page, VersionStatus.CheckedOut);
			}
		}

        protected override PageReferenceCollection GetChildrenReferences(PageReference pageLink, string languageID)
        {
			PageReferenceCollection collection = new PageReferenceCollection();
			foreach (var nodeId in NodeRepository().GetChildIdentities(pageLink.ToLinkString()))
			{
				collection.Add(ToPageReference(nodeId));
			}
            return collection;
        }

        protected override PageData GetLocalPage(PageReference pageLink, ILanguageSelector languageSelector)
        {
            Node node = NodeRepository().Get(ToIdentity(pageLink));
			if (node == null)
				throw new PageNotFoundException(pageLink);

            PageData page = new PageData(pageLink);
			InitializePageData(page, node.PageName, node.PageTypeName, node.Id.ExternalId, ToPageReference(node.Id), ParseStoreLink(node.ParentLink), new string[] { "en" });
			page["PageChangedBy"] = string.IsNullOrEmpty(node.ChangedBy) ? "someone" : node.ChangedBy;
			page["PageCreatedBy"] = string.IsNullOrEmpty(node.CreatedBy) ? "someone" : node.CreatedBy;
			page["PagePeerOrder"] = node.PeerOrder;
			page["PageChildOrderRule"] = node.ChildOrderRule;

			var bag = PropertyRepository(page.PageTypeID).GetOrCreatePropertyBag(node.Id);
			SetPropertyValues(page, ToDictionary(bag));
            return page;
        }

        protected override Uri ResolveLocalPage(PageReference pageLink, out Guid guid)
        {
			var node = NodeRepository().Get(ToIdentity(pageLink));
            if(node == null)
            {
				guid = Guid.Empty;
				return null;
			}

			guid = node.Id.ExternalId;
			return new Uri(UriSupport.AddQueryString(node.LinkURL, "id", pageLink.ToString()), UriKind.RelativeOrAbsolute);
        }

        protected override Uri ResolveLocalPage(Guid pageGuid, out PageReference pageLink)
        {
			var node = NodeRepository().Get(pageGuid);
            if(node == null)
            {
				pageLink = PageReference.EmptyReference;
				return null;
			}

			pageLink = ToPageReference(node.Id);
			return new Uri(UriSupport.AddQueryString(node.LinkURL, "id", pageLink.ToString()), UriKind.RelativeOrAbsolute);
        }

        #endregion

        public override void Delete(PageReference pageLink, bool forceDelete)
        {
            DeleteChildren(pageLink, forceDelete);

			var nodeRepository = NodeRepository();
			var node = nodeRepository.Get(ToIdentity(pageLink));
			if (node == null)
				return;

			var propertyRepository = PropertyRepository(node.PageTypeID);
			var bag = propertyRepository.GetByNodeId(node.Id);
			if (bag != null)
				propertyRepository.Delete(bag.Id);

			nodeRepository.Delete(node.Id);
        }

        public override void DeleteChildren(PageReference pageLink, bool forceDelete)
        {
            var children = GetChildrenReferences(pageLink, null);
            foreach (var child in children)
            {
                Delete(child, forceDelete);
            }
        }

        public override PageData GetDefaultPageData(PageReference parentPageLink, int pageTypeID, ILanguageSelector selector)
        {
            var page = base.GetDefaultPageData(parentPageLink, pageTypeID, selector);
            page["PageCreatedBy"] = Utility.CurrentUser();
            return page;
        }

		public override void Move(PageReference pageLink, PageReference destinationLink)
		{
			var repository = NodeRepository();
			var node = repository.Get(ToIdentity(pageLink));
			var previousLink = node.ParentLink;
			node.ParentLink = destinationLink.ToLinkString();
			
			if (destinationLink.ID == -1)
				AlterIsDeletedRecursive(repository, node, true);
			else if (node.IsDeleted)
				AlterIsDeletedRecursive(repository, node, false);
			else
				repository.Save(node);
		}

		private void AlterIsDeletedRecursive(NodeRepository repository, Node node, bool isDeleted)
		{
			node.IsDeleted = isDeleted;
			foreach (var child in repository.GetChildren(ToPageReference(node.Id).ToLinkString()).ToList())
			{
				AlterIsDeletedRecursive(repository, child, isDeleted);
			}
			repository.Save(node);
		}

		//string[] nodeProperties = typeof(Node).GetProperties().Select(p => p.Name).ToArray();

		//public override PageDataCollection FindAllPagesWithCriteria(PageReference pageLink, PropertyCriteriaCollection criterias, string languageBranch, ILanguageSelector selector)
		//{
		//    PageDataCollection pages = new PageDataCollection();

		//    var nodeRepository = NodeRepository();
			
		//    foreach (PropertyCriteria pc in criterias)
		//    {
				
		//    }

		//    foreach(var type in this.pageTypeSource())
		//    {
		//        var propertyStore = propertyMapper.GetStore(type);
		//        if(propertyStore != null)
		//        {
		//            var propertyRepository = new PropertyRepository(propertyStore);
		//            // add

		//        }
		//    }

		//    return base.FindAllPagesWithCriteria(pageLink, criterias, languageBranch, selector);
		//}

        internal void Initialize(DynamicDataStoreFactory storeFactory, Func<PageTypeCollection> pageTypeSource)
        {
            this.storeFactory = storeFactory;
            this.pageTypeSource = pageTypeSource;
            this.propertyMapper = new PropertyMapper(Name, storeFactory);
		}



		private NodeRepository NodeRepository()
		{

			return GetOrCreate<NodeRepository>("NodeRepository", 
				() => new NodeRepository(storeFactory.GetStore(typeof(Node)) ?? storeFactory.CreateStore(typeof(Node))));
		}

		private PropertyRepository PropertyRepository(int pageTypeID)
		{
			return GetOrCreate<PropertyRepository>("DdsProperties" + pageTypeID,
				() => new PropertyRepository(propertyMapper.GetOrCreateStore(pageTypeSource().First(pt => pt.ID == pageTypeID))));
		}

		private T GetOrCreate<T>(string key, Func<T> factory) where T: class
		{
			T value = null;
			if (HttpContext.Current != null)
				value = HttpContext.Current.Items[key] as T;
			if (value == null)
				value = factory();
			if (HttpContext.Current != null)
				HttpContext.Current.Items[key] = value;

			return value;
		}



		static Dictionary<string, string> ToDictionary(PropertyBag bag)
		{
			var dictionary = new Dictionary<string, string>();
			foreach (var key in bag.Keys)
			{
				if (bag[key] != null)
				{
					string value = bag[key].ToString();
					if (value.StartsWith("PL_"))
						value = value.Substring(3);
					dictionary[key] = value;
				}
			}
			return dictionary;
		}

		PageReference ToPageReference(Identity id)
		{
			return ToPageReference(id.StoreId);
		}

		PageReference ToPageReference(long storeId)
		{
			return new PageReference((int)storeId, Name);
		}

		static Identity ToIdentity(PageReference id)
		{
			return Identity.Parse(id.ID + ":" + Guid.Empty);
		}

		static PageReference ParseStoreLink(string pageLink)
		{
			if (string.IsNullOrEmpty(pageLink))
				return PageReference.EmptyReference;

			return PageReference.Parse(pageLink.Substring(3));
		}
    }
}